// guess_digit
package main

import (
	"bufio"
	"fmt"
	"math"
	"math/rand"
	"os"
	"time"

	"strconv"

	"github.com/golang-set"
)

// 定义了一个新的类型 ErrorCode
type ErrorCode int

// 定义了枚举值
const (
	EC_SUCCESS  ErrorCode = 0
	EC_LENGTH   ErrorCode = 1
	EC_NOTDIGIT ErrorCode = 2
)

// 给enum添加输出方法，把枚举数字常量变换成字符串。
func (p ErrorCode) String() string {
	switch p {
	case EC_SUCCESS:
		return "success"
	case EC_LENGTH:
		return "数据长度不正确"
	case EC_NOTDIGIT:
		return "请输入0~9之间的数字"
	default:
		return "UNKNOWN"
	}
}

func If(condition bool, trueVal, falseVal interface{}) interface{} {
	if condition {
		return trueVal
	}
	return falseVal
}

const RANDBITS_MAX = 10
const RANDBITS_MIN = 1

func digit_2_slice(value int, bits int) []int {
	var v [RANDBITS_MAX + 2]int
	v1 := value
	// 从数字里边构造vector
	for idx := 0; idx < bits; idx++ {
		v[bits-idx-1] = value % 10
		//		v[idx] = value % 10
		value /= 10
	}
	if false {
		fmt.Println(v1, v)
	}
	return v[:bits]
}

// 检查输入的数据是否符合要求
//	input	data - 输入数据
//			length - 要求的输入数据的长度
// 	ouput	[]int - 将data分解成int的数组，按照输入的先后顺序，例如’123’，返回[1,2,3]
//			ErrorCode - 如果检查失败，返回错误码
func check_input(data []byte, length int) ([]int, ErrorCode) {
	var v [RANDBITS_MAX + 2]int
	bits := len(data)

	if len(data) != length {
		return []int{}, EC_LENGTH // 数字长度不正确,返回空的切片和错误信息
	}

	for idx := 0; idx < bits; idx++ {
		if data[idx] < '0' || data[idx] > '9' {
			return []int{}, EC_NOTDIGIT // 数字内容不正确,返回空的切片和错误信息
		} else {
			v[idx] = int(data[idx] - 0x30)
		}
	}
	return v[:bits], EC_SUCCESS
}

// 计算数据的正确性
//	input	data - 原始生成的数据
//			input - 输入的数据。用input的数据对比data的数据位
// 	ouput	int - a的个数 位置和数字都对的个数
//			int - b的个数 数字对、位置不对的个数
func calc_input(data []int, input []int) (int, int) {
	lens := len(data)
	a := 0 // 位置和数字都对的个数
	b := 0 // 数字对、位置不对的个数

	data1 := make([]int, lens)
	copy(data1, data)
	for i := 0; i < lens; i++ {
		if data[i] == input[i] {
			a++
			continue
		}

		for j := 0; j < lens; j++ {
			if input[i] == data1[j] {
				b++
				data1[j] = -1
				continue
			}
		}
	}

	return a, b
}

// 生成随机数数组切片
// bits - 数字长度
// repeat - 数字是否可以重复
func random_number(bits int, repeat bool) []int {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	bits = If(bits > RANDBITS_MAX, RANDBITS_MAX, bits).(int)
	bits = If(bits < RANDBITS_MIN, RANDBITS_MIN, bits).(int)
	mask := int(math.Pow10(bits))

	var v [RANDBITS_MAX]int

	if repeat {
		// 数字可以重复；则用随机数创建整个数字，在拆分成数组，返回切片
		value := r.Int() % mask
		return digit_2_slice(value, bits)
	} else {
		// 数字可以重复；则用随机数创建每一位数字，存放在数组中，并用set测试是否重复。最终返回切片
		var v_count = 0
		var s = mapset.NewSet()
		for idx := 0; s.Cardinality() < bits; idx++ {
			rv := r.Intn(10)
			v[v_count] = rv

			// 用set来去重，比较set和vector的size，来判断新写的数据是否有效
			s.Add(rv)
			if s.Cardinality() != v_count {
				v_count++
			}
		}

		// 从vector里边构造数字
		value := 0
		for idx := 0; idx < bits; idx++ {
			value = value*10 + v[idx]
		}
		//		fmt.Println(value, v)
		return v[:bits]
	}

}

// 猜数字对外接口函数：以A角色随机生成一组数组，让B角色（用户）来猜测
func guess_digit_a(bits int) {
	values := random_number(bits, false)
	reader := bufio.NewReader(os.Stdin)

	times := 0 // 猜测次数

	fmt.Println(values)
	fmt.Printf("已经生成了一个%d位的不重复的随机数，请猜测('q' or 'x' to exit):", bits)
	for {
		data, _, _ := reader.ReadLine()
		if len(data) > 0 && (data[0] == 'q' || data[0] == 'x') {
			fmt.Printf("QUIT\n")
			return
		}

		// 检查输入是否为数字
		inputs, err := check_input(data, bits)
		if err != EC_SUCCESS {
			fmt.Printf("输入错误(%v)，请重新输入：", err)
			continue
		}

		times++

		// 计算输入结果
		a, b := calc_input(values, inputs)
		if a == len(values) {
			fmt.Printf("第<%d>次猜测结果为:【正确】。输入%v\n", times, inputs)
			return
		} else {
			fmt.Printf("第<%d>次猜测结果为:【%dA%dB】。输入%v。请再次输入猜测数字：", times, a, b, inputs)
		}
	}
}

// 随机生成一个数字，输入一个数字看是否匹对，匹配则结速，反之提示是大了还是小了
func guess_digit_between(min int, max int) {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	value := r.Intn(1+max-min) + min

	reader := bufio.NewReader(os.Stdin)
	times := 0 // 猜测次数

	fmt.Println(value)
	return
	fmt.Printf("已经生成了一个在范围 [%d,%d] 的随机数，请猜测('q' or 'x' to exit):", min, max)
	for {
		data, _, _ := reader.ReadLine()
		if len(data) > 0 && (data[0] == 'q' || data[0] == 'x') {
			fmt.Printf("QUIT\n")
			return
		}

		inputs, err := strconv.Atoi(string(data)) //string to int,并作输入格式判断
		if err != nil {
			fmt.Println("格式不对，请输入数字")
		}

		times++

		// 计算输入结果
		if inputs == value {
			fmt.Printf("第<%d>次猜测结果为:【正确】。输入为%v\n", times, inputs)
			break
		} else if inputs < value {
			fmt.Printf("第<%d>次猜测结果为:【偏小】。输入为%v，请再次输入", times, inputs)
		} else {
			fmt.Printf("第<%d>次猜测结果为:【偏大】。输入为%v，请再次输入", times, inputs)
		}
	}
}

// func main() {
// 	guess_digit_between(0, 20)
// 	return
// }
